/*


栈内存从高位地址向下增长，且栈内存是连续分配的，

一般来说操作系统对栈内存的大小都有限制，因此 C 语言中无法创建任意长度的数组。
在 Rust 中，main 线程的栈大小是 8MB，普通线程是 2MB，
在函数调用时会在其中创建一个临时栈空间，调用结束后 Rust 会让这个栈空间里的对象自动进入 Drop 流程，
    最后栈顶指针自动移动到上一个调用栈顶，无需程序员手动干预，因而栈内存申请和释放是非常高效的。

堆内存通常只受物理内存限制，而且通常是不连续的，因此从性能的角度看，栈往往比堆更高。

*/
/*
    Rust 堆上对象还有一个特殊之处，它们都拥有一个所有者，因此受所有权规则的限制：
    当赋值时，发生的是所有权的转移（只需浅拷贝栈上的引用或智能指针即可）
*/

#[cfg(test)]
mod const_test {
    #[test]
    fn main() {
        // a 是 String 类型，它其实是一个智能指针结构体，该智能指针存储在函数栈中，指向堆上的字符串数据。
        // 当被从 foo 函数转移给 main 中的 b 变量时，
        // 栈上的智能指针被复制一份赋予给 b，而底层数据无需发生改变，这样就完成了所有权从 foo 函数内部到 b 的转移。
        let b = foo("world");
        println!("{}", b);
    }

    fn foo(x: &str) -> String {
        let a = "Hello, ".to_string() + x;
        a
    }
}

/*

    由于 Box 是简单的封装，除了将值存储在堆上外，并没有其它性能上的损耗

    特意的将数据分配在堆上
    数据较大时，又不想在转移所有权时进行数据拷贝
    类型的大小在编译期无法确定，但是我们又需要固定大小的类型时
    特征对象，用于说明对象实现了一个特征，而不是某个特定的类型
     Box<T> 将数据存储在堆上
*/
#[cfg(test)]
mod box_test {
    #[test]
    fn main() {
        // 栈上数据转移所有权时，实际上是把数据拷贝了一份
        let aa = 3; //栈上
        let bb = aa; // 所有权： a->b 栈上 a copy to b
        println!("{} {}", aa, bb);

        // 转移所有权仅仅是复制一份栈中的指针
        let a = Box::new(3);
        let b = a; // 所有权： a->b 堆上 value -> a to  value -> b, a -> nil
        println!("b= {}", b); // 3
        // println!("{} {}", a, b); // err

        // 下面一行代码将报错
        // let b = a + 1; // cannot add `{integer}` to `Box<{integer}>`
    }

    // dst 递归 编译时无法知道具体的大小
    enum List {
        // Cons(i32, List), //err
        Cons(i32, Box<List>),
        Nil,
    }

    // 想实现不同类型组成的数组只有两个办法：枚举和特征对象，前者限制较多，因此后者往往是最常用的解决办法。
    trait Version {
        fn version(&self) -> String;
    }

    #[derive(Debug)]
    struct QQ;

    #[derive(Debug)]
    struct WeChat;

    impl Version for QQ {
        fn version(&self) -> String {
            String::from("QQ Version")
        }
    }

    impl Version for WeChat {
        fn version(&self) -> String {
            String::from("WeChat Version")
        }
    }

    #[test]
    fn trait_obj() {
        // 指定类型 dyn
        let elems: Vec<Box<dyn Version>> = vec![Box::new(QQ {}), Box::new(WeChat {})];

        for x in elems {
            println!("{}", x.version())
        }
    }
}

/*
Vec<i32> 内存布局

(stack)    (heap)
┌──────┐   ┌───┐
│ vec1 │──→│ 1 │
└──────┘   ├───┤
           │ 2 │
           ├───┤
           │ 3 │
           ├───┤
           │ 4 │
           └───┘

Vec<Box<i32>> 的内存布局

                    (heap)
(stack)    (heap)   ┌───┐
┌──────┐   ┌───┐ ┌─→│ 1 │
│ vec2 │──→│B1 │─┘  └───┘
└──────┘   ├───┤    ┌───┐
           │B2 │───→│ 2 │
           ├───┤    └───┘
           │B3 │─┐  ┌───┐
           ├───┤ └─→│ 3 │
           │B4 │─┐  └───┘
           └───┘ │  ┌───┐
                 └─→│ 4 │
                    └───┘
*/
// Deref 可以让智能指针像引用那样工作，这样你就可以写出同时支持智能指针和引用的代码，例如 *T
// Drop 允许你指定智能指针超出作用域后自动执行的代码，例如做一些数据清除等收尾工作

#[cfg(test)]
mod box_deref {
    #[test]
    fn main() {
        let arr = vec![Box::new(1), Box::new(2)];
        let (first, second) = (&arr[0], &arr[1]); //使用 & 借用数组中的元素，否则会报所有权错误

        //表达式不能隐式的解引用，因此必须使用 ** 做两次解引用，
        // 第一次将 &Box<i32> 类型转成 Box<i32>，第二次将 Box<i32> 转成 i32
        let sum = **first + **second;
    }

    // Box 一个非常有用的关联函数：Box::leak，它可以消费掉 Box 并且强制目标值从内存中泄漏
    #[test]
    fn box_leak() {
        let s = get_global_config();
        // 标注的 'static 只是用来忽悠(帮助)编译器的，
        // 但是超出作用域，一样被释放回收。而使用 Box::leak 就可以将一个运行期的值转为 'static
        println!("{}", s);
    }

    // String 创建于函数中，那么返回它的唯一方法就是转移所有权给调用者 fn move_str() -> String，
    // 而通过 Box::leak 我们不仅返回了一个 &str 字符串切片，它还是 'static 生命周期的！
    fn get_global_config() -> &'static str {
        let mut s = String::new();
        s.push_str("hello, world");

        Box::leak(s.into_boxed_str()) // Rc/Arc 也可以实现此功能，但是 Box::leak 是性能最高的。
    }
}

/*

Box 背后是调用 jemalloc 来做内存管理，所以堆上的空间无需我们的手动管理。
与此类似，带 GC 的语言中的对象也是借助于 Box 概念来实现的，一切皆对象 = 一切皆 Box， 只不过我们无需自己去 Box 罢了。
*/
